﻿using System;
using System.Collections.Generic;
using System.Text;

namespace RaisingStudio.Data.Common
{
    /// <summary>
    /// database transaction manager.
    /// </summary>
    public class TransactionManager : RaisingStudio.Data.Common.ITransactionManager
    {
        /// <summary>
        /// Dictionary of transactions.
        /// </summary>
        protected Dictionary<string, System.Data.IDbTransaction> transactions = new Dictionary<string, System.Data.IDbTransaction>();
        /// <summary>
        /// Stack of transactions.
        /// </summary>
        protected Stack<string> transactionStack = new Stack<string>();

        /// <summary>
        /// has Transactional.
        /// </summary>
        public bool HasTransaction
        {
            get
            {
                return this.transactions.Count > 0 ? true : false;
            }
        }

        protected System.Data.IDbTransaction defaultTransaction;
        /// <summary>
        /// default Transactional.
        /// </summary>
        public System.Data.IDbTransaction DefaultTransaction
        {
            get
            {
                return this.defaultTransaction;
            }
        }

        protected string defaultTransactionID;
        /// <summary>
        /// default The ID of transaction.
        /// </summary>
        public string DefaultTransactionID
        {
            get
            {
                return this.defaultTransactionID;
            }
        }

        protected System.Data.IDbConnection connection;
        /// <summary>
        /// database connection.
        /// </summary>
        public System.Data.IDbConnection Connection
        {
            get
            {
                return this.connection;
            }
        }

        protected System.Data.ConnectionState previousConnectionState;
        /// <summary>
        /// connection state.
        /// </summary>
        public System.Data.ConnectionState PreviousConnectionState
        {
            get
            {
                return this.previousConnectionState;
            }
        }

        protected bool connectionToBeClosed = false;
        /// <summary>
        /// close the connection or not.
        /// </summary>
        public bool ConnectionToBeClosed
        {
            get
            {
                return this.connectionToBeClosed;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="connection">database connection.</param>
        public TransactionManager(System.Data.IDbConnection connection)
        {
            this.connection = connection;
        }

        /// <summary>
        /// Begin Database transaction.
        /// </summary>
        /// <returns>The ID of transaction.</returns>
        public virtual string BeginTransaction()
        {
            string transactionID = null;
            try
            {
                this.previousConnectionState = this.connection.State;
                // 打开连接。
                if ((this.connection.State & System.Data.ConnectionState.Open) != System.Data.ConnectionState.Open)
                {
                    this.connection.Open();
                    this.connectionToBeClosed = true;
                }
                System.Data.IDbTransaction transaction = this.connection.BeginTransaction();
                transactionID = System.Guid.NewGuid().ToString();
                // Add 到事务字典中。
                this.transactions.Add(transactionID, transaction);
                if ((this.defaultTransactionID != null) && (this.defaultTransactionID != string.Empty))
                {
                    // 压入Stack of transactions.
                    this.transactionStack.Push(this.defaultTransactionID);
                }
                // Sets 为default Transactional.
                this.defaultTransactionID = transactionID;
                this.defaultTransaction = transaction;
            }
            catch //(Exception ex)
            {
                throw;
            }
            return transactionID;
        }

        /// <summary>
        /// Begin Database transaction.
        /// </summary>
        /// <param name="isolationLevel">Specifies the isolation level for the transaction.</param>
        /// <returns>The ID of transaction.</returns>
        public virtual string BeginTransaction(System.Data.IsolationLevel isolationLevel)
        {
            string transactionID = null;
            try
            {
                this.previousConnectionState = this.connection.State;
                // 打开连接。
                if ((this.connection.State & System.Data.ConnectionState.Open) != System.Data.ConnectionState.Open)
                {
                    this.connection.Open();
                    this.connectionToBeClosed = true;
                }
                System.Data.IDbTransaction transaction = this.connection.BeginTransaction(isolationLevel);
                transactionID = System.Guid.NewGuid().ToString();
                // Add 到事务字典中。
                this.transactions.Add(transactionID, transaction);
                if ((this.defaultTransactionID != null) && (this.defaultTransactionID != string.Empty))
                {
                    // 压入Stack of transactions.
                    this.transactionStack.Push(this.defaultTransactionID);
                }
                // Sets 为default Transactional.
                this.defaultTransactionID = transactionID;
                this.defaultTransaction = transaction;
            }
            catch //(Exception ex)
            {
                throw;
            }
            return transactionID;
        }

        /// <summary>
        /// Commit Database transaction.
        /// </summary>
        public virtual void CommitTransaction()
        {
            if ((this.defaultTransactionID != null) && (this.defaultTransaction != null))
            {
                try
                {
                    this.defaultTransaction.Commit();
                }
                finally
                {
                    // 从事务字典中移除。
                    this.transactions.Remove(this.defaultTransactionID);
                    if (this.transactionStack.Count > 0)
                    {
                        // 弹出Stack of transactions.
                        this.defaultTransactionID = this.transactionStack.Pop();
                        this.defaultTransaction = GetTransaction(this.defaultTransactionID);
                    }
                    else
                    {
                        // 清空default Transactional.
                        this.defaultTransactionID = null;
                        this.defaultTransaction = null;

                        // 关闭连接。
                        if (this.connectionToBeClosed)
                        {
                            this.connection.Close();
                        }
                    }
                }
            }
            else
            {
                throw new System.InvalidOperationException("Please begin the transaction first..");
            }
        }

        /// <summary>
        /// Commit Database transaction.
        /// </summary>
        /// <param name="transactionID">The ID of transaction.</param>
        public virtual void CommitTransaction(string transactionID)
        {
            if (transactionID != null)
            {
                if (transactionID == this.defaultTransactionID)
                {
                    // default Transactional.
                    CommitTransaction();
                }
                else
                {
                    System.Data.IDbTransaction transaction = this.transactions[transactionID];
                    if (transaction != null)
                    {
                        try
                        {
                            transaction.Commit();
                        }
                        finally
                        {
                            // 从事务字典中移除。
                            this.transactions.Remove(transactionID);
                        }
                    }
                    else
                    {
                        throw new System.ArgumentException("The given transactionID is not correct.");
                    }
                }
            }
            else
            {
                throw new System.ArgumentNullException("The given transactionID can not be null.");
            }
        }

        /// <summary>
        /// Rollback transaction.
        /// </summary>
        public virtual void RollbackTransaction()
        {
            if ((this.defaultTransactionID != null) && (this.defaultTransaction != null))
            {
                try
                {
                    this.defaultTransaction.Rollback();
                }
                finally
                {
                    // 从事务字典中移除。
                    this.transactions.Remove(this.defaultTransactionID);
                    if (this.transactionStack.Count > 0)
                    {
                        // 弹出Stack of transactions.
                        this.defaultTransactionID = this.transactionStack.Pop();
                        this.defaultTransaction = GetTransaction(this.defaultTransactionID);
                    }
                    else
                    {
                        // 清空default Transactional.
                        this.defaultTransactionID = null;
                        this.defaultTransaction = null;

                        // 关闭连接。
                        if (this.connectionToBeClosed)
                        {
                            this.connection.Close();
                        }
                    }
                }
            }
            else
            {
                throw new System.InvalidOperationException("Please begin the transaction first..");
            }
        }

        /// <summary>
        /// Rollback transaction.
        /// </summary>
        /// <param name="transactionID">The ID of transaction.</param>
        public virtual void RollbackTransaction(string transactionID)
        {
            if (transactionID != null)
            {
                if (transactionID == this.defaultTransactionID)
                {
                    // default Transactional.
                    RollbackTransaction();
                }
                else
                {
                    System.Data.IDbTransaction transaction = this.transactions[transactionID];
                    if (transaction != null)
                    {
                        try
                        {
                            transaction.Rollback();
                        }
                        finally
                        {
                            // 从事务字典中移除。
                            this.transactions.Remove(transactionID);
                        }
                    }
                    else
                    {
                        throw new System.ArgumentException("The given transactionID is not correct.");
                    }
                }
            }
            else
            {
                throw new System.ArgumentNullException("The given transactionID can not be null.");
            }
        }

        /// <summary>
        /// Gets Transactional.
        /// </summary>
        /// <param name="transactionID">The ID of transaction.</param>
        /// <returns>Database transaction.</returns>
        public virtual System.Data.IDbTransaction GetTransaction(string transactionID)
        {
            if (transactionID != null)
            {
                System.Data.IDbTransaction transaction = this.transactions[transactionID];
                if (transaction != null)
                {
                    return transaction;
                }
                else
                {
                    throw new System.ArgumentException("The given transactionID is not correct.");
                }
            }
            else
            {
                throw new System.ArgumentNullException("The given transactionID can not be null.");
            }
        }
    }
}
